﻿namespace DomainServices.Tools
{
    using DomainServices.Tools.SharedTypes;
    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.ServiceModel;
    using System.ServiceModel.DomainServices;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;
    using System.ServiceModel.Web;

    internal class DomainServiceProxyGenerator : ProxyGenerator
    {
        private DomainServiceDescription _domainServiceDescription;
        private IDictionary<Type, CodeTypeDeclaration> _typeMapping;
        private const string DefaultActionSchema = "http://tempuri.org/{0}/{1}";
        private const string DefaultFaultActionSchema = "http://tempuri.org/{0}/{1}{2}";
        private const string DefaultReplyActionSchema = "http://tempuri.org/{0}/{1}Response";

        public DomainServiceProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, DomainServiceDescription domainServiceDescription, IDictionary<Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator)
        {
            this._domainServiceDescription = domainServiceDescription;
            this._typeMapping = typeMapping;
        }

        internal static string DomainContextTypeName(DomainServiceDescription domainServiceDescription)
        {
            string name = domainServiceDescription.DomainServiceType.Name;
            if (name.EndsWith("Service", StringComparison.Ordinal))
            {
                name = name.Substring(0, name.Length - 7) + "Context";
            }
            return name;
        }

        private void GenEntityContainer(CodeTypeDeclaration proxyClass, IEnumerable<Type> entityTypes, DomainServiceDescription domainServiceDescription)
        {
            CodeTypeDeclaration declaration = this.GenEntityContainerInnerClass(proxyClass, entityTypes, domainServiceDescription);
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "CreateEntityContainer",
                Attributes = MemberAttributes.Family | MemberAttributes.Override,
                ReturnType = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.EntityContainer", proxyClass.UserData["Namespace"] as string, false)
            };
            method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(DomainServices.Tools.Resource.CodeGen_CreateEntityContainer_Method_Summary_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(DomainServices.Tools.Resource.CodeGen_CreateEntityContainer_Method_Returns_Comment, base.ClientProxyGenerator.IsCSharp));
            CodeObjectCreateExpression expression = new CodeObjectCreateExpression(CodeGenUtilities.GetTypeReference((proxyClass.UserData["Namespace"] as string) + "." + proxyClass.Name + "." + declaration.Name, proxyClass.UserData["Namespace"] as string, true), new CodeExpression[0]);
            CodeMethodReturnStatement statement = new CodeMethodReturnStatement(expression);
            method.Statements.Add(statement);
            proxyClass.Members.Add(method);
        }

        private CodeTypeDeclaration GenEntityContainerInnerClass(CodeTypeDeclaration proxyClass, IEnumerable<Type> entityTypes, DomainServiceDescription domainServiceDescription)
        {
            string name = base.ClientProxyGenerator.GetNamespace(proxyClass).Name;
            CodeTypeDeclaration declaration = CodeGenUtilities.CreateTypeDeclaration(proxyClass.Name + "EntityContainer", name);
            declaration.BaseTypes.Add(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.EntityContainer", name, false));
            declaration.TypeAttributes = TypeAttributes.Sealed;
            proxyClass.Members.Add(declaration);
            CodeConstructor constructor = new CodeConstructor {
                Attributes = MemberAttributes.Public
            };
            declaration.Members.Add(constructor);
            HashSet<Type> set = new HashSet<Type>();
            foreach (Type type in entityTypes)
            {
                set.Add(type);
            }
            using (IEnumerator<Type> enumerator2 = (from t in entityTypes
                orderby t.FullName
                select t).GetEnumerator())
            {
                System.Func<Type, bool> func = null;
                Type entityType;
                while (enumerator2.MoveNext())
                {
                    entityType = enumerator2.Current;
                    if (func == null)
                    {
                        func = t => (t != entityType) && t.IsAssignableFrom(entityType);
                    }
                    if (!Enumerable.Any<Type>(set, func))
                    {
                        CodeTypeReference reference = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.EntitySetOperations", name, false);
                        CodeExpression left = null;
                        bool flag = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Insert);
                        bool flag2 = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Update);
                        bool flag3 = domainServiceDescription.IsOperationSupported(entityType, DomainOperation.Delete);
                        CodeTypeReferenceExpression targetObject = new CodeTypeReferenceExpression(reference);
                        if ((!flag && !flag2) && !flag3)
                        {
                            left = new CodeFieldReferenceExpression(targetObject, "None");
                        }
                        else if ((flag && flag2) && flag3)
                        {
                            left = new CodeFieldReferenceExpression(targetObject, "All");
                        }
                        else
                        {
                            if (flag)
                            {
                                left = new CodeFieldReferenceExpression(targetObject, "Add");
                            }
                            if (flag2)
                            {
                                CodeFieldReferenceExpression right = new CodeFieldReferenceExpression(targetObject, "Edit");
                                if (left == null)
                                {
                                    left = right;
                                }
                                else
                                {
                                    left = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BitwiseOr, right);
                                }
                            }
                            if (flag3)
                            {
                                CodeFieldReferenceExpression expression4 = new CodeFieldReferenceExpression(targetObject, "Remove");
                                if (left == null)
                                {
                                    left = expression4;
                                }
                                else
                                {
                                    left = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BitwiseOr, expression4);
                                }
                            }
                        }
                        CodeTypeReference reference2 = CodeGenUtilities.GetTypeReference(entityType, base.ClientProxyGenerator, proxyClass);
                        CodeMethodReferenceExpression method = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "CreateEntitySet", new CodeTypeReference[] { reference2 });
                        CodeMethodInvokeExpression expression6 = new CodeMethodInvokeExpression(method, new CodeExpression[] { left });
                        constructor.Statements.Add(expression6);
                    }
                }
            }
            return declaration;
        }

        public override void Generate()
        {
            CodeTypeDeclaration proxyClass;
            Type domainServiceType = this._domainServiceDescription.DomainServiceType;
            CodeNamespace orGenNamespace = base.ClientProxyGenerator.GetOrGenNamespace(domainServiceType);
            AttributeCollection source = this._domainServiceDescription.Attributes;
            if (orGenNamespace != null)
            {
                string typeName = DomainContextTypeName(this._domainServiceDescription);
                proxyClass = CodeGenUtilities.CreateTypeDeclaration(typeName, domainServiceType.Namespace);
                proxyClass.IsPartial = true;
                proxyClass.TypeAttributes = TypeAttributes.Sealed | TypeAttributes.Public;
                orGenNamespace.Types.Add(proxyClass);
                CodeTypeReference reference = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.DomainContext", orGenNamespace.Name, false);
                proxyClass.BaseTypes.Add(reference);
                string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_Class_Summary_Comment, new object[] { domainServiceType.Name });
                proxyClass.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
                CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeType, new object[] { ex.Message, proxyClass.Name, ex.InnerException.Message }), source.Cast<Attribute>(), proxyClass.CustomAttributes, proxyClass.Comments);
                NotificationMethodGenerator generator = new NotificationMethodGenerator(base.ClientProxyGenerator);
                proxyClass.Members.AddRange(generator.PartialMethodsSnippetBlock);
                CodeTypeDeclaration contractInterface = this.GenerateContract(proxyClass);
                EnableClientAccessAttribute enableClientAccessAttribute = source.OfType<EnableClientAccessAttribute>().Single<EnableClientAccessAttribute>();
                this.GenerateConstructors(proxyClass, contractInterface, enableClientAccessAttribute, generator.OnCreatedMethodInvokeExpression);
                new DomainOperationEntryProxyGenerator(base.ClientProxyGenerator, proxyClass, this._domainServiceDescription).Generate();
                new InvokeOperationProxyGenerator(base.ClientProxyGenerator, proxyClass, this._domainServiceDescription).Generate();
                this.GenEntityContainer(proxyClass, this._domainServiceDescription.RootEntityTypes, this._domainServiceDescription);
                this._typeMapping[domainServiceType] = proxyClass;
            }
        }

        private static CodeConstructor GenerateConstructor(CodeTypeDeclaration proxyClass, IEnumerable<CodeParameterDeclarationExpression> parameters, IEnumerable<CodeExpression> baseParameters, CodeCommentStatementCollection comments, bool callBaseCtr)
        {
            CodeConstructor constructor = new CodeConstructor();
            proxyClass.Members.Add(constructor);
            constructor.Attributes = MemberAttributes.Public;
            if (parameters != null)
            {
                foreach (CodeParameterDeclarationExpression expression in parameters)
                {
                    constructor.Parameters.Add(expression);
                }
            }
            if (baseParameters != null)
            {
                foreach (CodeExpression expression2 in baseParameters)
                {
                    if (callBaseCtr)
                    {
                        constructor.BaseConstructorArgs.Add(expression2);
                    }
                    else
                    {
                        constructor.ChainedConstructorArgs.Add(expression2);
                    }
                }
            }
            if (comments != null)
            {
                constructor.Comments.AddRange(comments);
            }
            return constructor;
        }

        private void GenerateConstructors(CodeTypeDeclaration proxyClass, CodeTypeDeclaration contractInterface, EnableClientAccessAttribute enableClientAccessAttribute, CodeMethodInvokeExpression onCreatedExpression)
        {
            CodeTypeReference createType = CodeGenUtilities.GetTypeReference(typeof(Uri), base.ClientProxyGenerator, proxyClass);
            CodeTypeReference type = CodeGenUtilities.GetTypeReference(typeof(UriKind), base.ClientProxyGenerator, proxyClass);
            string containingNamespace = proxyClass.UserData["Namespace"] as string;
            CodeTypeReference reference3 = CodeGenUtilities.GetTypeReference(containingNamespace + "." + proxyClass.Name + "." + contractInterface.Name, containingNamespace, true);
            string str2 = string.Format(CultureInfo.InvariantCulture, "{0}.svc", new object[] { this._domainServiceDescription.DomainServiceType.FullName.Replace('.', '-') });
            CodeExpression expression = new CodeObjectCreateExpression(createType, new CodeExpression[] { new CodePrimitiveExpression(str2), new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(type), "Relative") });
            CodeExpression expression2 = null;
            if (!enableClientAccessAttribute.RequiresSecureEndpoint)
            {
                CodeTypeReference reference4 = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.WebDomainClient", proxyClass.UserData["Namespace"] as string, false);
                reference4.TypeArguments.Add(reference3);
                expression2 = new CodeObjectCreateExpression(reference4, new CodeExpression[] { expression });
            }
            else
            {
                CodeTypeReference reference5 = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.WebDomainClient", proxyClass.UserData["Namespace"] as string, false);
                reference5.TypeArguments.Add(reference3);
                expression2 = new CodeObjectCreateExpression(reference5, new CodeExpression[] { expression, new CodePrimitiveExpression(true) });
            }
            List<CodeParameterDeclarationExpression> parameters = null;
            List<CodeExpression> baseParameters = new List<CodeExpression>(1) {
                expression2
            };
            CodeCommentStatementCollection comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Default_Constructor_Summary_Comments, new object[] { proxyClass.Name }), base.ClientProxyGenerator.IsCSharp);
            GenerateConstructor(proxyClass, parameters, baseParameters, comments, false);
            parameters = new List<CodeParameterDeclarationExpression>(1) {
                new CodeParameterDeclarationExpression(createType, "serviceUri")
            };
            baseParameters = new List<CodeExpression>(1);
            if (!enableClientAccessAttribute.RequiresSecureEndpoint)
            {
                CodeTypeReference reference6 = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.WebDomainClient", proxyClass.UserData["Namespace"] as string, false);
                reference6.TypeArguments.Add(reference3);
                baseParameters.Add(new CodeObjectCreateExpression(reference6, new CodeExpression[] { new CodeArgumentReferenceExpression("serviceUri") }));
            }
            else
            {
                CodeTypeReference reference7 = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.WebDomainClient", proxyClass.UserData["Namespace"] as string, false);
                reference7.TypeArguments.Add(reference3);
                baseParameters.Add(new CodeObjectCreateExpression(reference7, new CodeExpression[] { new CodeArgumentReferenceExpression("serviceUri"), new CodePrimitiveExpression(true) }));
            }
            comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_ConstructorComments_Summary_ServiceUri, new object[] { proxyClass.Name }), base.ClientProxyGenerator.IsCSharp);
            comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("serviceUri", string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_ConstructorComments_Param_ServiceUri, new object[] { this._domainServiceDescription.DomainServiceType.Name }), base.ClientProxyGenerator.IsCSharp));
            GenerateConstructor(proxyClass, parameters, baseParameters, comments, false);
            parameters = new List<CodeParameterDeclarationExpression>(1) {
               new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.DomainClient", proxyClass.UserData["Namespace"] as string, false), "domainClient")
            };
            baseParameters = new List<CodeExpression>(1) {
                new CodeArgumentReferenceExpression("domainClient")
            };
            comments = CodeGenUtilities.GenerateSummaryCodeComment(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_ConstructorComments_Summary_DomainClientAccumulating, new object[] { proxyClass.Name }), base.ClientProxyGenerator.IsCSharp);
            comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("domainClient", string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_ConstructorComments_Param_DomainClient, new object[0]), base.ClientProxyGenerator.IsCSharp));
            GenerateConstructor(proxyClass, parameters, baseParameters, comments, true).Statements.Add(onCreatedExpression);
        }

        private CodeTypeDeclaration GenerateContract(CodeTypeDeclaration proxyClass)
        {
            string name = this._domainServiceDescription.DomainServiceType.Name;
            CodeTypeDeclaration declaration = CodeGenUtilities.CreateTypeDeclaration("I" + name + "Contract", proxyClass.UserData["Namespace"] as string);
            proxyClass.Members.Add(declaration);
            string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Summary_Comment, new object[] { name });
            declaration.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            declaration.IsInterface = true;
            declaration.CustomAttributes.Add(CodeGenUtilities.CreateAttributeDeclaration(typeof(ServiceContractAttribute), base.ClientProxyGenerator, proxyClass));
            HashSet<Type> registeredTypes = new HashSet<Type>();
            foreach (DomainOperationEntry entry in from op in Enumerable.Where<DomainOperationEntry>(this._domainServiceDescription.DomainOperationEntries, delegate (DomainOperationEntry op) {
                if ((op.Operation != DomainOperation.Query) && (op.Operation != DomainOperation.Invoke))
                {
                    return op.Operation == DomainOperation.Custom;
                }
                return true;
            })
                orderby op.Name
                select op)
            {
                if (entry.Operation == DomainOperation.Custom)
                {
                    this.GenerateContractServiceKnownTypes(declaration, entry, registeredTypes);
                }
                else
                {
                    this.GenerateContractMethod(declaration, entry);
                }
            }
            if (Enumerable.Where<DomainOperationEntry>(this._domainServiceDescription.DomainOperationEntries, delegate (DomainOperationEntry op) {
                if (((op.Operation != DomainOperation.Delete) && (op.Operation != DomainOperation.Insert)) && (op.Operation != DomainOperation.Update))
                {
                    return op.Operation == DomainOperation.Custom;
                }
                return true;
            }).Any<DomainOperationEntry>())
            {
                this.GenerateContractSubmitChangesMethod(declaration);
            }
            return declaration;
        }

        private void GenerateContractMethod(CodeTypeDeclaration contractInterface, DomainOperationEntry operation)
        {
            string name = this._domainServiceDescription.DomainServiceType.Name;
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "Begin" + operation.Name,
                ReturnType = CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), base.ClientProxyGenerator, contractInterface)
            };
            contractInterface.Members.Add(method);
            string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Summary_Comment, new object[] { operation.Name });
            method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            foreach (DomainOperationParameter parameter in operation.Parameters)
            {
                comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Parameter_Comment, new object[] { parameter.Name });
                method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment(parameter.Name, comment, base.ClientProxyGenerator.IsCSharp));
            }
            method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Callback_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("asyncState", DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_AsyncState_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Returns_Comment, base.ClientProxyGenerator.IsCSharp));
            this.GenerateContractMethodAttributes(contractInterface, method, name, operation.Name);
            foreach (DomainOperationParameter parameter2 in operation.Parameters)
            {
                method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(parameter2.ParameterType), base.ClientProxyGenerator, contractInterface), parameter2.Name));
            }
            method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(AsyncCallback), base.ClientProxyGenerator, contractInterface), "callback"));
            method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(object), base.ClientProxyGenerator, contractInterface), "asyncState"));
            CodeMemberMethod method2 = new CodeMemberMethod {
                Name = "End" + operation.Name
            };
            bool hasSideEffects = true;
            string str3 = null;
            if (operation.Operation == DomainOperation.Query)
            {
                hasSideEffects = ((QueryAttribute) operation.OperationAttribute).HasSideEffects;
                str3 = "QueryResult";
                if (operation.ReturnType == typeof(void))
                {
                    method2.ReturnType = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.QueryResult", contractInterface.UserData["Namespace"] as string, false);
                }
                else
                {
                    method2.ReturnType = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.QueryResult", contractInterface.UserData["Namespace"] as string, false);
                    method2.ReturnType.TypeArguments.Add(CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(operation.AssociatedType), base.ClientProxyGenerator, contractInterface));
                }
            }
            else
            {
                if (operation.Operation == DomainOperation.Invoke)
                {
                    hasSideEffects = ((InvokeAttribute) operation.OperationAttribute).HasSideEffects;
                }
                str3 = CodeGenUtilities.TranslateType(operation.ReturnType).Name;
                method2.ReturnType = CodeGenUtilities.GetTypeReference(CodeGenUtilities.TranslateType(operation.ReturnType), base.ClientProxyGenerator, contractInterface, false);
            }
            if (!hasSideEffects)
            {
                CodeAttributeDeclaration declaration = CodeGenUtilities.CreateAttributeDeclaration(typeof(WebGetAttribute), base.ClientProxyGenerator, contractInterface);
                method.CustomAttributes.Add(declaration);
            }
            comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_End_Method_Summary_Comment, new object[] { method.Name });
            method2.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_End_Method_Parameter_Comment, new object[] { method.Name });
            method2.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("result", comment, base.ClientProxyGenerator.IsCSharp));
            if (operation.ReturnType != typeof(void))
            {
                comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_End_Method_Returns_Comment, new object[] { str3, operation.Name });
                method2.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            }
            contractInterface.Members.Add(method2);
            method2.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), base.ClientProxyGenerator, contractInterface), "result"));
        }

        private void GenerateContractMethodAttributes(CodeTypeDeclaration contractInterface, CodeMemberMethod beginQueryMethod, string domainServiceName, string operationName)
        {
            CodeAttributeDeclaration declaration = CodeGenUtilities.CreateAttributeDeclaration(typeof(OperationContractAttribute), base.ClientProxyGenerator, contractInterface);
            declaration.Arguments.Add(new CodeAttributeArgument("AsyncPattern", new CodePrimitiveExpression(true)));
            declaration.Arguments.Add(new CodeAttributeArgument("Action", new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, "http://tempuri.org/{0}/{1}", new object[] { domainServiceName, operationName }))));
            declaration.Arguments.Add(new CodeAttributeArgument("ReplyAction", new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, "http://tempuri.org/{0}/{1}Response", new object[] { domainServiceName, operationName }))));
            beginQueryMethod.CustomAttributes.Add(declaration);
            string name = typeof(DomainServiceFault).Name;
            CodeAttributeDeclaration declaration2 = CodeGenUtilities.CreateAttributeDeclaration(typeof(FaultContractAttribute), base.ClientProxyGenerator, contractInterface);
            declaration2.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.DomainServiceFault", contractInterface.UserData["Namespace"] as string, false))));
            declaration2.Arguments.Add(new CodeAttributeArgument("Action", new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, "http://tempuri.org/{0}/{1}{2}", new object[] { domainServiceName, operationName, name }))));
            declaration2.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(name)));
            declaration2.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression("DomainServices")));
            beginQueryMethod.CustomAttributes.Add(declaration2);
        }

        private void GenerateContractServiceKnownTypes(CodeTypeDeclaration contractInterface, DomainOperationEntry operation, HashSet<Type> registeredTypes)
        {
            List<Attribute> attributes = new List<Attribute>();
            foreach (DomainOperationParameter parameter in operation.Parameters)
            {
                Type nonNullableType = System.ServiceModel.DomainServices.TypeUtility.GetNonNullableType(CodeGenUtilities.TranslateType(parameter.ParameterType));
                if (System.ServiceModel.DomainServices.TypeUtility.IsPredefinedListType(nonNullableType) || System.ServiceModel.DomainServices.TypeUtility.IsComplexTypeCollection(nonNullableType))
                {
                    Type elementType = System.ServiceModel.DomainServices.TypeUtility.GetElementType(nonNullableType);
                    if (elementType != null)
                    {
                        nonNullableType = elementType.MakeArrayType();
                    }
                }
                if (!registeredTypes.Contains(nonNullableType) && this.TypeRequiresRegistration(nonNullableType))
                {
                    registeredTypes.Add(nonNullableType);
                    if (nonNullableType.IsEnum && base.ClientProxyGenerator.NeedToGenerateEnumType(nonNullableType))
                    {
                        base.ClientProxyGenerator.RegisterUseOfEnumType(nonNullableType);
                        nonNullableType = new VirtualType(nonNullableType.Name, CodeGenUtilities.TranslateNamespace(nonNullableType, base.ClientProxyGenerator), nonNullableType.Assembly, nonNullableType.BaseType);
                    }
                    attributes.Add(new ServiceKnownTypeAttribute(nonNullableType));
                }
            }
            if (attributes.Count > 0)
            {
                CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, contractInterface, attributes, contractInterface.CustomAttributes, contractInterface.Comments, true);
            }
        }

        private void GenerateContractSubmitChangesMethod(CodeTypeDeclaration contractInterface)
        {
            string name = this._domainServiceDescription.DomainServiceType.Name;
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "BeginSubmitChanges",
                ReturnType = CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), base.ClientProxyGenerator, contractInterface)
            };
            contractInterface.Members.Add(method);
            string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Summary_Comment, new object[] { "SubmitChanges" });
            method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("changeSet", DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_SubmitMethod_Changeset_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("callback", DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Callback_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("asyncState", DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_AsyncState_Parameter_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_Begin_Method_Returns_Comment, base.ClientProxyGenerator.IsCSharp));
            this.GenerateContractMethodAttributes(contractInterface, method, name, "SubmitChanges");
            CodeTypeReference type = CodeGenUtilities.GetTypeReference("System.Collections.Generic.IEnumerable", contractInterface.UserData["Namespace"] as string, false);
            type.TypeArguments.Add(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.ChangeSetEntry", contractInterface.UserData["Namespace"] as string, false));
            method.Parameters.Add(new CodeParameterDeclarationExpression(type, "changeSet"));
            method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(AsyncCallback), base.ClientProxyGenerator, contractInterface), "callback"));
            method.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(object), base.ClientProxyGenerator, contractInterface), "asyncState"));
            CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.DomainServiceFault", contractInterface.UserData["Namespace"] as string, false).TypeArguments.Add(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.ChangeSetEntry", contractInterface.UserData["Namespace"] as string, false));
            CodeMemberMethod method2 = new CodeMemberMethod {
                Name = "EndSubmitChanges",
                ReturnType = type
            };
            contractInterface.Members.Add(method2);
            comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_End_Method_Summary_Comment, new object[] { "BeginSubmitChanges" });
            method2.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_End_Method_Parameter_Comment, new object[] { "BeginSubmitChanges" });
            method2.Comments.AddRange(CodeGenUtilities.GenerateParamCodeComment("result", comment, base.ClientProxyGenerator.IsCSharp));
            method2.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(DomainServices.Tools.Resource.CodeGen_DomainContext_ServiceContract_End_SubmitMethod_Returns_Comment, base.ClientProxyGenerator.IsCSharp));
            method2.Parameters.Add(new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(typeof(IAsyncResult), base.ClientProxyGenerator, contractInterface), "result"));
        }

        private bool TypeRequiresRegistration(Type type)
        {
            if (type.IsPrimitive || (type == typeof(string)))
            {
                return false;
            }
            if (this._domainServiceDescription.EntityTypes.Contains<Type>(type))
            {
                return false;
            }
            return true;
        }
    }
}

